import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class State {
	public static final char EPSILON = 0;
	private HashMap<Character, LinkedList<State>> transitions;
	private boolean accept;
	private String token;
	
	public State(boolean accept) {
		this.accept = accept;
		transitions = new HashMap<Character, LinkedList<State>>();
	}
	
	/**
	 * Generate a set containing the states in the epsilon closure of this state 
	 * @return the epsilon closure of this state
	 */
	public Set<State> epsilonClosure() {
		//Just BFS the graph created by only epsilon edges, returning the visited set.
		List<State> queue = new LinkedList<State>();
		Set<State> visited = new HashSet<State>();
		queue.add(this);
		State cur;
		while(!queue.isEmpty()) {
			cur = queue.remove(0);
			visited.add(cur);
			for(State s : cur.transition(EPSILON)) {
				if(!visited.contains(s))
					queue.add(s);
			}
		}
		
		return visited;
	}

	/**
	 * Return a list of the states reachable by consuming c
	 * @param c the character to consume
	 * @return the list of reachable states
	 */
	public List<State> transition(char c) {
		if(transitions.get(c) != null)
			return transitions.get(c);
		else
			return new LinkedList<State>();
	}

	
	/**
	 * Add a transition on char c to the given state.
	 * @param c the char to transition on
	 * @param state the state to transition to
	 */
	public void addTransition(char c, State state) {
		LinkedList<State> l = transitions.get(c);
		if(l == null) {
			l = new LinkedList<State>();
			transitions.put(c, l);
		}
		l.add(state);		
	}
	
	/**
	 * Convenience method for adding an epsilon transition from this state to the given state.
	 * @param state the state to add the transition to
	 */
	public void addEpsilonTransition(State state) {
		addTransition(EPSILON, state);
	}
	
	public boolean isAccept() {
		return accept;
	}
	
	public void setAccept(boolean accept) {
		this.accept = accept;
	}
	
	public HashMap<Character, LinkedList<State>> getTransitions() {
		return transitions;
	}

	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}
	
	public String toString() {
		return token + " " + accept;
	}
	

	
	
}